﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Super.Common
{
     
    /// <summary> Url
    /// 
    /// ====== 前台使用 ======= 
    /// 
    /// 继承原有参数,新增或修改制定参数,返回参数部分
    /// <a href="http://xxx.不想看到.com/con/act/@URL.Get("a=123,b=abc,c=ddd")"></a>
    /// 
    /// 继承原有参数,新增或修改指定参数,返回完整路径.
    /// <a href="@URL.GetFull("key","value","key1","value!@#*&*1")"></a>
    /// 
    /// 忽略原有参数,返回完整URL
    /// <a href="@URL.NewFull("p=clear")"></a>
    /// 
    /// 清除所有参数,返回URL
    /// <a href="@URL.ClearParams()"></a>
    /// 
    /// ====== 后台使用 =======
    /// 
    /// var key = URL.Query("key");
    /// 
    /// var paramList = URL.Query();
    /// 
    /// 
    /// </summary> 
    public static class URL
    {
        #region 测试代码

#if DEBUG

        private static bool IsFirstTest = true;
        private static void Test()
        {
            if (!IsFirstTest)
            {
                return;
            }
            IsFirstTest = false;

            string url = Test("Http://shop.不想看斗.com/admin?n=3&d=7", "a=1");

            string url1 = Test(url, "b=2");

            string url2 = Test(url1, "asd=fds");

            string url3 = TestParam(url2, "ddd=ddd");

            string end = "TestEnd";
        }


        /// <summary> 测试代码
        /// </summary>
        /// <returns></returns>
        private static string Test(string url, params string[] keyValue)
        {
            StaticUrl = url;
            return GetFull(false, keyValue);
        }

        private static string TestParam(string url, params string[] keyValue)
        {
            StaticUrl = url;
            return Get(false, keyValue);
        }

#endif
        #endregion
        #region 私有成员


        /// <summary>获取当前URL
        /// </summary>
        /// <returns></returns>
        private static string GetUrl()
        {
            return HttpContext.Current.Request.Url.ToString(); 
        }

        private static string StaticUrl;

        /// <summary> 拆分Url 0=url  1=param 2=?key=value
        /// </summary>
        /// <returns></returns>
        private static string[] SplitUrl(string url, bool isNeedUrlQueryString)
        {
            if (url == "")
            {
                return new string[] { "", "", "" };
            }

            url = url.TrimEnd('/');

            var rt = new string[3];

            var lp = url.Split('?');

            if (lp.Length > 1 && isNeedUrlQueryString)
            {
                rt[2] = "?" + lp[1];
            }
            else
            {
                rt[2] = "";
            }

            var lpinx = lp[0].LastIndexOf('/');


            var parm = lp[0].Substring(lpinx + 1);
            if (lpinx < 8)
            {
                //没有参数
                parm = "";
            }

            if (ParamDic.CheckParam(parm))
            {
                rt[1] = parm;
                rt[0] = lp[0].Remove(lpinx);
            }
            else
            {
                rt[1] = "";
                rt[0] = lp[0];
            }

            return rt;
        }


        #endregion

        /// <summary> 返回 Url 参数部分. 在原有参数上修改, 如果不存在,则附加.
        /// </summary>
        /// <param name="isNeedUrlQueryString"></param>
        /// <param name="keyValue"></param>
        /// <param name="fullUrl"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string Get(out string fullUrl, string url, bool isNeedUrlQueryString, params string[] keyValue)
        {
            //验证parm是否为参数级别
            var spUrl = SplitUrl(url, isNeedUrlQueryString);

            Dictionary<string, string> dc = spUrl[1] != "" ? ParamDic.GetParams(spUrl[1]) : new Dictionary<string, string>();

            //传入的唯一字符串? 再解析一下把.
            if (keyValue.Length == 1)
            {
                var ls = keyValue[0].Split(',');
                var rs = new List<string>();
                foreach (var item in ls)
                {
                    rs.AddRange(item.Split('='));
                }
                keyValue = rs.ToArray();
            }

            if (keyValue.Length % 2 == 0)
            {
                //设置
                for (int i = 0; i < keyValue.Length; i += 2)
                {
                    dc[keyValue[i]] = keyValue[i + 1];
                }
            }
            else
            {
                if (keyValue.Length != 1)
                {
                    throw new Exception("keyValue 必须成对出现!");
                }
            }

            var rt = ParamDic.ToUrl(dc);
            spUrl[1] = rt == "" ? "" : "/" + rt;
            fullUrl = spUrl[0] + spUrl[1] + spUrl[2];
            return rt + spUrl[2];
        }

        /// <summary>  返回 Url 参数部分. 在原有参数上修改, 如果不存在,则附加.
        /// </summary>
        public static string Get(params string[] keyValue)
        {
            return Get(true, keyValue);
        }

        /// <summary>  返回 Url 参数部分. 在原有参数上修改, 如果不存在,则附加.
        /// </summary>
        /// <param name="isNeedUrlQueryString">是否需要返回UrlQueryString( 问号后面的部分 )</param>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static string Get(bool isNeedUrlQueryString, params string[] keyValue)
        {
            string s;
            return Get(out s, GetUrl(), isNeedUrlQueryString, keyValue);
        }

        /// <summary> 返回完整Url. 在原 Url 上修改参数, 如果不存在, 则附加. 
        /// </summary>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static string GetFull(params string[] keyValue)
        {
            return GetFull(true, keyValue);
        }

        /// <summary> 返回完整Url. 在原 Url 上修改参数, 如果不存在, 则附加. 
        /// </summary>
        /// <param name="keyValue"></param>
        /// <param name="isNeedUrlQueryString">是否需要返回获取Url参数 问号后面的.</param>
        /// <returns></returns>
        public static string GetFull(bool isNeedUrlQueryString, params string[] keyValue)
        {
            string s;
            Get(out s, GetUrl(), isNeedUrlQueryString, keyValue);
            return s;
        }

        /// <summary> 返回新的 Url 参数部分. 忽略原有参数.
        /// </summary>
        /// <returns></returns>
        public static string New(params string[] keyValue)
        {
            return New(true, keyValue);
        }

        /// <summary> 返回新的 Url 参数部分. 忽略原有参数.
        /// </summary>
        /// <param name="isNeedUrlQueryString">是否需要返回UrlQueryString( 问号后面的部分 )</param>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static string New(bool isNeedUrlQueryString, params string[] keyValue)
        {
            string s;
            return Get(out s, "", isNeedUrlQueryString, keyValue);
        }

        /// <summary> 返回新的完整Url 并忽略原有参数.
        /// </summary>
        /// <returns></returns>
        public static string NewFull(params string[] keyValue)
        {
            return NewFull(true, keyValue);
        }

        /// <summary> 返回新的完整Url 并忽略原有参数.
        /// </summary>
        /// <param name="isNeedUrlQueryString">是否需要返回UrlQueryString( 问号后面的部分 )</param>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static string NewFull(bool isNeedUrlQueryString, params string[] keyValue)
        {
            var ls = SplitUrl(GetUrl(), isNeedUrlQueryString);
            ls[1] = "/" + New(isNeedUrlQueryString, keyValue);
            return ls[0] + ls[1] + ls[2];
        }

        /// <summary> 移除指定key  返回完整Url
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static string Remove(params string[] keys)
        {
            return Remove(true, keys);
        }

        /// <summary> 移除指定key  返回完整Url
        /// </summary>
        /// <param name="isNeedUrlQueryString">是否需要返回UrlQueryString( 问号后面的部分 )</param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static string Remove(bool isNeedUrlQueryString, params string[] keys)
        {
            var v = Query();
            foreach (var item in keys)
            {
                v.Remove(item);
            }
            var ls = SplitUrl(GetUrl(), isNeedUrlQueryString);

            if (v.Count > 0)
            {
                return ls[0] + "/" + ParamDic.ToUrl(v) + ls[2];
            }
            return ls[0] + ls[2];
        }

        /// <summary> 清空参数
        /// </summary>
        /// <param name="isNeedUrlQueryString">是否需要返回UrlQueryString( 问号后面的部分 )</param>
        /// <returns></returns>
        public static string ClearParams(bool isNeedUrlQueryString = true)
        {
            var ls = SplitUrl(GetUrl(), isNeedUrlQueryString);
            return ls[0] + ls[2];
        }

        /// <summary> 获取参数
        /// </summary> 
        /// <param name="key"></param>
        /// <param name="isReturnNull"></param>
        /// <param name="isReturnUrlGetParam"></param>
        /// <returns></returns>
        public static string Query(string key, bool isReturnNull = true, bool isReturnUrlGetParam = false)
        {
            var v = HttpContext.Current.Request[key];
            if (v != null)
            {
                return v;
            }

            var q = Query();
            if (q.ContainsKey(key))
            {
                return q[key];
            }
            if (isReturnUrlGetParam)
            {
                var rt = HttpContext.Current.Request[key];
                return rt ?? (isReturnNull ? null : "");
            }
            return isReturnNull ? null : "";
        }

        /// <summary> 获取参数集合
        /// </summary>
        /// <returns></returns>
        public static Dictionary<string, string> Query()
        {
            var spUrl = SplitUrl(GetUrl(), false);
            if (spUrl[1] != "")
            {
                return ParamDic.GetParams(spUrl[1]);
            }
            return new Dictionary<string, string>();
        }
    }

    /// <summary> 参数处理类.(仅供 URL 调用?)
    /// </summary>
    internal static class ParamDic
    {
        #region 私有成员

        /// <summary> 转数字表
        /// </summary>
        private static List<char> pass = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY".ToList();

        /// <summary> 转义表
        /// </summary>
        private static List<char> pk = "~!@#$%^&*()_+`-=\t\r\n,.<>/?;':\"[]{}\\|Z".ToList();

        /// <summary> 转换为Url参数
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string ToParam(string str)
        {
            string s = LenToChar(str.Length);
            foreach (char item in str)
                if ((char.IsNumber(item) || char.IsLower(item) || char.IsUpper(item)) && item != 'Z')
                    s += item; //无需转义
                else
                {
                    var inx = pk.IndexOf(item);//转义.
                    if (inx < 0)
                        throw new Exception("未知的字符! 请添加");
                    s += "Z" + LenToChar(inx);
                }
            return s;
        }

        /// <summary> 根据Url获取参数
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string ReadParam(ref string str)
        {
            var len = pass.IndexOf(str[0]);
            string s = "";
            int inx = 1;
            for (int i = 0; i < len; i++)
            {
                if (str[inx] != 'Z')
                    s += str[inx].ToString();
                else
                    s += pk[pass.IndexOf(str[++inx])];
                inx++;
            }
            str = str.Substring(inx);
            return s;
        }

        #endregion

        /// <summary> lenToString 
        /// </summary>
        /// <param name="len"></param>
        /// <returns></returns>
        public static string LenToChar(int len)
        {
            if (len < pass.Count)
                return pass[len].ToString();
            throw new Exception("暂不支持 > 60的数字");
        }

        /// <summary> 获得参数校验值.
        /// </summary>
        public static string GetParamCheckValue(string paramStr)
        {
            if (paramStr.Length < 2)
            {
                return "";
            }

            var sum = paramStr.Aggregate(0, (current, t) => current + t);
            return LenToChar((sum % 3000) / 60) + LenToChar(sum % 60);
        }

        /// <summary> 校验参数是否合法.[简单校验]
        /// </summary>
        /// <param name="paramStr"></param>
        /// <returns></returns>
        public static bool CheckParam(string paramStr)
        {
            return paramStr.Length > 2 && GetParamCheckValue(paramStr.Substring(2)) == paramStr.Remove(2);
        }

        /// <summary> 转换为Url
        /// </summary>
        /// <param name="keyValue">key1,value1,key2,value2,key3,value3.....</param>
        /// <returns></returns>
        public static string ToUrl(params string[] keyValue)
        {
            string s = keyValue.Aggregate("", (current, item) => current + ToParam(item));
            return GetParamCheckValue(s) + s;
        }

        /// <summary> 转换为Url
        /// </summary>
        /// <param name="keyValue"></param>
        /// <returns></returns>
        public static string ToUrl(Dictionary<string, string> keyValue)
        {
            var s = keyValue.Aggregate("", (current, item) => current + (ToParam(item.Key) + ToParam(item.Value)));
            return GetParamCheckValue(s) + s;
        }

        /// <summary> 根据Url获取参数
        /// </summary>
        /// <param name="paramStr"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetParams(string paramStr)
        {
            paramStr = paramStr.Substring(2);

            var rt = new Dictionary<string, string>();
            while (paramStr.Length > 1)
            {
                var key = ReadParam(ref paramStr);
                var value = ReadParam(ref paramStr);
                rt.Add(key, value);
            }
            return rt;
        }
    }

}